【C++】模板声明与定义不分离 |
您所在的位置:网站首页 › cpp 函数定义 › 【C++】模板声明与定义不分离 |
一般在写C++相关代码的时候,我们总习惯于将类声明和类实现进行分离。也就是说,类的声明一般写在.h文件中,而它的实现一般写在.cpp文件中。但是,在模板类中,这个习惯却要恰恰相反。即:要求模板类的类声明和类实现要都放在头文件,而不能分离。 本文就对模板的这个奇特习惯进行分析。 分离式编译模式在进行模板特性的讲解之前,首先需要了解一下C++的分离式编译模式。 所谓分离编译模式,就是指:一个程序或者项目由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件连接起来形成单一的可执行文件的过程。 C/C++组织源代码和生成可执行文件的方式就是分离式编译模式。 简单粗暴的理解就是,一个C++项目分为若干个cpp文件和h文件,每个cpp文件单独编译成每个的目标文件,最终将每个cpp文件连接在一起组成最后的单一的可执行文件。这里最重要的点就是:编译是相对于每个cpp文件而言的。 接下去的问题就是,对于编译每个cpp文件的时候,是否都需要每个类的实现? 如果都需要每个类的实现,那么就只能将每个类的实现也都写到h文件中,这样在cpp文件中引入的h文件中,才会有每个类的实现;如果不需要每个类的实现,那么就没有必要将每个类的实现写到h文件中。 C/C++所采用的方法是:只要给出类的声明,就可以在本源文件中使用该类。由于每个源文件都是独立的编译单元,在当前源文件中使用但未在此类的实现,就假设在其他的源文件中实现好了。 模板声明与定义 声明定义不分离但是,分离式编译模式却驯不服模板。 C++标准要求编译器在实例化模板时,必须在上下文中可以查看到其实现;而反过来,在看到实例化模板之前,编译器对模板的实现是不处理的。原因很简单,编译器怎么会预先知道typename实参是什么呢?因此模板的实例化与实现必须放到同一文件中。 《C++编程思想》说明了原因: 模板定义很特殊。由template 处理的任何东西都意味着编译器在当时不为它分配存储空间,它一直处于等待状态直到被一个模板实例告知。在编译器和连接器的某一处,有一机制能去掉指定模板的多重定义。所以为了容易使用,几乎总是在头文件中放置全部的模板声明和定义。 简单来说:只有模板实例化时,编译器才会得知T实参是什么。而编译器在处理模板实例化时,不仅仅要看到模板的定义式,还需要模版的实现体。 为什么需要这样呢? 比如说存在类Rect, 其类定义式写在test.h,类的实现体写在test.cpp中。对于模板来说,编译器在处理test.cpp文件时,编译器无法预知T的实参是什么,所以编译器对其实现是不作处理的。 紧接着在main.cpp中用到了Rect,这个时候会实例化。也就是说,在test.h中会实例出对Rect进行类的声明。但是,由于分离式编译模式,在编译的时候只需要类的声明即可,因此编译是没有任何问题的。 但是在链接的过程中,需要找到Rect的实现部分。但是上面也说了,编译是相对于每个cpp文件而言的。在test.cpp的编译的时候,由于不知道T的实参是什么,并没有对其进行处理。因此,Rect的实现自然并没有被编译,链接也就自然而然地因找不到而出错。 也就是说,模板如果将类声明和类实现进行分离,那么分离式编译模式会导致在链接的时候出现问题。 例子解释清楚了,接下来可以看一个例子: 在test.h文件中,定义模板类Rect: #include template class Rect { public: Rect(T l = 0.0f, T t = 0.0f, T r = 0.0f, T b = 0.0f) : left_(l), top_(t), right_(r), bottom_(b) {} void display(); T left_; T top_; T right_; T bottom_; };在test.cpp文件中,定义模板类Rect方法的实现: #include "test.h" template void Rect::display() { std::cout |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |